Dynamic wakes¶
For spatially uniform timeseries input data, foxes can compute dynamic wake propagation. This in principle works by following a flow trace backwards in time for each point of interest, and identifying it with a wake trajectory if it hits a rotor.
Since all foxes computations are based on chunks of input states, this concept only works if
either all states fall into a single chunk,
or the
Iterativealgorithm is used for the calculation.
The later is necessary in case the wake originates from a state previous to the chunk of evaluation, since the default Downwind algorithm does not allow cross-chunk communication during the calculation.
These are the inlcudes for this example:
In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.rcParams["animation.html"] = "jshtml"
import foxes
import foxes.variables as FV
import foxes.constants as FC
We create a case with a regular 3 x 3 wind farm layout:
In [2]:
mbook = foxes.models.ModelBook()
states = foxes.input.states.Timeseries(
data_source="timeseries_100.csv.gz",
output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
var2col={FV.WS: "ws", FV.WD: "wd", FV.TI: "ti"},
fixed_vars={FV.RHO: 1.225, FV.TI: 0.07},
)
farm = foxes.WindFarm()
foxes.input.farm_layout.add_grid(
farm,
xy_base=np.array([0.0, 0.0]),
step_vectors=np.array([[1000.0, 0], [0, 800.0]]),
steps=(3, 3),
turbine_models=["DTU10MW"],
verbosity=0
)
algo = foxes.algorithms.Iterative(
mbook,
farm,
states=states,
rotor_model="centre",
wake_models=["Bastankhah_linear_k004"],
wake_frame="timelines",
partial_wakes_model="auto",
chunks={FC.STATE: 500, FC.POINT: 5000},
verbosity=1
)
Notice the wake frame choice timelines, which is a pre-defined instance of the class Timelines from the model book.
Let’s run the wind farm calculation:
In [3]:
with foxes.utils.runners.DaskRunner() as runner:
farm_results = runner.run(algo.calc_farm)
Algorithm Iterative: Iteration 0
[########################################] | 100% Completed | 102.50 ms
[########################################] | 100% Completed | 103.40 ms
Algorithm Iterative: Iteration 1
[########################################] | 100% Completed | 102.62 ms
[########################################] | 100% Completed | 102.25 ms
DefaultConv: Convergence check
REWS: delta = 1.324e-01, lim = 1.000e-05 -- FAILED
TI : delta = 0.000e+00, lim = 1.000e-06 -- OK
Algorithm Iterative: Iteration 2
[########################################] | 100% Completed | 102.41 ms
[########################################] | 100% Completed | 107.42 ms
DefaultConv: Convergence check
REWS: delta = 0.000e+00, lim = 1.000e-05 -- OK
TI : delta = 0.000e+00, lim = 1.000e-06 -- OK
Algorithm Iterative: Convergence reached.
Notice the iterations and the convergence behaviour. Now the farm results are ready:
In [4]:
farm_df = farm_results.to_dataframe()
print("\nFarm results data:\n")
print(farm_df[[FV.AMB_REWS, FV.REWS, FV.P]])
Farm results data:
AMB_REWS REWS P
state turbine
2023-07-07 12:00:00 0 6.0 6.000000 1532.700000
1 6.0 6.000000 1532.700000
2 6.0 6.000000 1532.700000
3 6.0 5.063574 845.738040
4 6.0 5.063574 845.738040
... ... ... ...
2023-07-07 13:39:00 4 6.0 5.063574 845.738040
5 6.0 5.063574 845.738040
6 6.0 4.693421 640.016335
7 6.0 4.693421 640.016335
8 6.0 4.693421 640.016335
[900 rows x 3 columns]
This timeseries has a time step of 1 minute. Let’s visualize the wake dynamics in am animation:
In [5]:
with foxes.utils.runners.DaskRunner() as runner:
print("Computing animation data")
fig, ax = plt.subplots(figsize=(8, 7))
o = foxes.output.FlowPlots2D(algo, farm_results, runner=runner)
ims = []
for si, (fig, im) in enumerate(o.gen_states_fig_xy(
FV.WS,
resolution=30,
quiver_pars=dict(angles="xy", scale_units="xy", scale=0.013),
quiver_n=35,
xmax=5000,
ymax=5000,
fig=fig,
ax=ax,
ret_im=True,
title=None,
animated=True,
)):
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True,
repeat_delay=2000)
plt.close()
print("done.")
print("Creating animation")
ani
Computing animation data
[########################################] | 100% Completed | 102.96 ms
[########################################] | 100% Completed | 102.80 ms
[########################################] | 100% Completed | 37.18 s
done.
Creating animation
Out[5]:
For the fun of it, let’s re-run this case assuming the time step was 10 s instead of 1 min. We can do so by using the wake frame Timelines(dt_min=1/6), which is called timelines_10s in the model book:
In [6]:
algo.finalize(clear_mem=True)
algo = foxes.algorithms.Iterative(
mbook,
farm,
states=states,
rotor_model="centre",
wake_models=["Bastankhah_linear_k004"],
wake_frame="timelines_10s",
partial_wakes_model="auto",
chunks={FC.STATE: 500, FC.POINT: 5000},
verbosity=1
)
with foxes.utils.runners.DaskRunner() as runner:
farm_results = runner.run(algo.calc_farm)
print("Computing animation data")
fig, ax = plt.subplots(figsize=(8, 7))
o = foxes.output.FlowPlots2D(algo, farm_results, runner=runner)
ims = []
for si, (fig, im) in enumerate(o.gen_states_fig_xy(
FV.WS,
resolution=30,
quiver_pars=dict(angles="xy", scale_units="xy", scale=0.013),
quiver_n=35,
xmax=5000,
ymax=5000,
fig=fig,
ax=ax,
ret_im=True,
title="Delta t = 10 s",
animated=True,
)):
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True,
repeat_delay=2000)
plt.close()
print("done.")
print("Creating animation")
ani
Algorithm Iterative: Iteration 0
[########################################] | 100% Completed | 102.05 ms
[########################################] | 100% Completed | 203.73 ms
Algorithm Iterative: Iteration 1
[########################################] | 100% Completed | 101.74 ms
[########################################] | 100% Completed | 101.77 ms
DefaultConv: Convergence check
REWS: delta = 1.324e-01, lim = 1.000e-05 -- FAILED
TI : delta = 0.000e+00, lim = 1.000e-06 -- OK
Algorithm Iterative: Iteration 2
[########################################] | 100% Completed | 102.30 ms
[########################################] | 100% Completed | 102.89 ms
DefaultConv: Convergence check
REWS: delta = 0.000e+00, lim = 1.000e-05 -- OK
TI : delta = 0.000e+00, lim = 1.000e-06 -- OK
Algorithm Iterative: Convergence reached.
Computing animation data
[########################################] | 100% Completed | 101.66 ms
[########################################] | 100% Completed | 101.81 ms
[########################################] | 100% Completed | 49.08 s
done.
Creating animation
Out[6]:
In [ ]: